﻿using System;
using System.Collections;
using UnityEngine;


namespace Utils
{
    /// <summary>
    /// 
    /// </summary>
    public class TimeCoroutine : MonoSingletonBase<TimeCoroutine>
    {
        #region 启动/关闭

        /// <summary>
        /// 启动一个协程
        /// </summary>
        /// <param name="coroutine">协程枚举器</param>
        /// <returns>协程对象</returns>
        public static Coroutine Start(IEnumerator coroutine)
        {
            if (coroutine == null)
                throw new ArgumentNullException(nameof(coroutine));

            return Instance.StartCoroutine(coroutine);
        }

        /// <summary>
        /// 停止一个协程
        /// </summary>
        /// <param name="coroutine">协程对象</param>
        public static void Stop(Coroutine coroutine)
        {
            if (coroutine == null)
                return;

            Instance.StopCoroutine(coroutine);
        }

        #endregion

        #region 扩展 | 等待

        /// <summary>
        /// 等待异步操作完成
        /// </summary>
        /// <param name="asyncOperation">异步操作</param>
        /// <param name="value">回调函数</param>
        /// <returns>协程对象</returns>
        public static Coroutine Wait(AsyncOperation asyncOperation, Action value)
        {
            if (asyncOperation == null)
                throw new ArgumentNullException(nameof(asyncOperation));

            return Instance.StartCoroutine(Instance.IWait(asyncOperation, value));
        }
        #endregion

        /// <summary>
        /// 延迟指定时间后执行回调
        /// </summary>
        /// <param name="delayTime">延迟时间（秒）</param>
        /// <param name="callBack">回调函数</param>
        /// <returns>协程对象</returns>
        public static Coroutine Delay(float delayTime, Action callBack)
        {
            if (delayTime < 0)
                throw new ArgumentException("Delay time cannot be negative.", nameof(delayTime));

            return Instance.StartCoroutine(Instance.IDelay(delayTime, callBack));
        }

        /// <summary>
        /// 循环执行回调
        /// </summary>
        /// <param name="lerpTime">每次循环的间隔时间（秒）</param>
        /// <param name="callBack">每次循环的回调函数</param>
        /// <param name="finish">循环结束后的回调函数</param>
        /// <returns>协程对象</returns>
        public static Coroutine Loop(float lerpTime, Action callBack, Action finish = null)
        {
            if (lerpTime < 0)
                throw new ArgumentException("Lerp time cannot be negative.", nameof(lerpTime));

            return Instance.StartCoroutine(Instance.ILoop(lerpTime, callBack, finish));
        }

        /// <summary>
        /// 循环执行指定次数的回调
        /// </summary>
        /// <param name="count">循环次数</param>
        /// <param name="lerpTime">每次循环的间隔时间（秒）</param>
        /// <param name="callBack">每次循环的回调函数</param>
        /// <param name="finish">循环结束后的回调函数</param>
        /// <returns>协程对象</returns>
        public static Coroutine Count(int count, float lerpTime, Action callBack, Action finish = null)
        {
            if (count < 0)
                throw new ArgumentException("Count cannot be negative.", nameof(count));
            if (lerpTime < 0)
                throw new ArgumentException("Lerp time cannot be negative.", nameof(lerpTime));

            return Instance.StartCoroutine(Instance.ICount(count, lerpTime, callBack, finish));
        }

        /// <summary>
        /// 在指定时间内执行回调
        /// </summary>
        /// <param name="totalTime">总时间（秒）</param>
        /// <param name="lerpTime">每次回调的间隔时间（秒）</param>
        /// <param name="callBack">每次回调的函数</param>
        /// <param name="onCompleted">完成后的回调函数</param>
        /// <returns>协程对象</returns>
        public static Coroutine Time(float totalTime, float lerpTime, Action<float> callBack, Action onCompleted = null)
        {
            if (totalTime < 0)
                throw new ArgumentException("Total time cannot be negative.", nameof(totalTime));
            if (lerpTime < 0)
                throw new ArgumentException("Lerp time cannot be negative.", nameof(lerpTime));

            return Instance.StartCoroutine(Instance.ITime(totalTime, lerpTime, callBack, onCompleted));
        }

        private IEnumerator ILoop(float lerpTime, Action callBack, Action finish=null)
        {
            var waitTime = new WaitForSeconds(lerpTime);

            while (true)
            {
                try
                {
                    callBack?.Invoke();
                }
                catch (Exception ex)
                {
                    Debug.LogError($"Callback execution failed: {ex.Message}");
                }

                yield return waitTime;
            }
        }

        private IEnumerator ICount(int count, float lerpTime, Action callBack, Action finish)
        {
            var waitTime = new WaitForSeconds(lerpTime);
            int current = 0;

            while (current < count)
            {
                try
                {
                    callBack?.Invoke();
                }
                catch (Exception ex)
                {
                    Debug.LogError($"Callback execution failed: {ex.Message}");
                }

                current++;
                yield return waitTime;
            }

            try
            {
                finish?.Invoke();
            }
            catch (Exception ex)
            {
                Debug.LogError($"Finish callback execution failed: {ex.Message}");
            }
        }

        private IEnumerator IDelay(float delayTime, Action callBack)
        {
            if (delayTime > 0)
                yield return new WaitForSeconds(delayTime);

            try
            {
                callBack?.Invoke();
            }
            catch (Exception ex)
            {
                Debug.LogError($"Callback execution failed: {ex.Message}");
            }
        }

        private IEnumerator ITime(float totalTime, float lerpTime, Action<float> callBack, Action onCompleted)
        {
            var waitTime = new WaitForSeconds(lerpTime);
            float current = 0f;

            while (current < totalTime)
            {
                current = Mathf.Clamp(current + lerpTime, 0, totalTime);
                try
                {
                    callBack?.Invoke(current);
                }
                catch (Exception ex)
                {
                    Debug.LogError($"Callback execution failed: {ex.Message}");
                }

                yield return waitTime;
            }

            try
            {
                onCompleted?.Invoke();
            }
            catch (Exception ex)
            {
                Debug.LogError($"OnCompleted callback execution failed: {ex.Message}");
            }
        }

        private IEnumerator IWait(AsyncOperation asyncOperation, Action value)
        {
            yield return asyncOperation;

            try
            {
                value?.Invoke();
            }
            catch (Exception ex)
            {
                Debug.LogError($"Callback execution failed: {ex.Message}");
            }
        }
    }
}